package com.cqtczh.qhd.shiro;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;


/**
 * 自定义shiro用户认证和授权的Realm，同时AuthorizingRealm还提供了缓存认证信息和授权信息的能力
 * （AuthorizingRealm本身可对授权信息进行缓存，父类AuthenticatingRealm可以对认证信息缓存，
 * 具体的缓存配置请看spring-shiro.xml bean id="authenticationRealm"的配置）
 * 通过回调doGetAuthenticationInfo实现认证逻辑，回调doGetAuthorizationInfo实现授权逻辑
 * @Author xukai、venson_lew(modify)
 * Date 2016/1/26
 */
public class MyAuthenticationRealm  extends AuthorizingRealm {

    
    /**
     * 用户认证回调函数，用户登录且缓存中无用户认证信息时调用。
     * 凡是ShiroFilterFactoryBean的filterChainDefinitions属性中定义的需要authc的路径都会回调该函数进行认证
     * @author venson_lew(modify)
     * @date 2016-06-30
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        if(!"password".equals(new String(token.getPassword()))){
            throw new IncorrectCredentialsException();
        }
        //shiro session 管理会话
        SecurityUtils.getSubject().getSession(true).setAttribute("account","admin");
        //如果用户需要重新认证，则清空之前授权的信息
        this.clearCachedAuthorizationInfo("admin");
        Map<String,Object> principal = new HashMap<String, Object>();
        return new SimpleAuthenticationInfo(principal,"password",getName());
    }

    /**
     * 用户授权回调函数，即授予用户拥有哪些资源访问权限。
     * 该函数并非每次都会调用，进行鉴权但缓存中无用户的授权信息时调用
     * venson_lew(modify)
     * @date 2016-06-30
     */
    @SuppressWarnings("unchecked")
	@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Map<String,Object> principal = (Map<String,Object>)super.getAvailablePrincipal(principalCollection);
        principal.get("adminid");
        // 查询权限信息
        List<String> ps = new ArrayList<String>();
        if (ps == null || ps.size() < 1) {
            return null;
        }
        // 返回权限信息
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(ps);
        return info;
    }
    
    /**
     * 清空用户认证信息，<span style="color:red;">修改密码/用户信息时手动调用</span>
     * @param principal 已认证的用户，可以是Principle中的adminName或adminAccount
     * @author venson_lew
     * @date 2016-06-30
     */
    
    public void clearCachedAuthenticationInfo(String principal) {
    	SimplePrincipalCollection siplePrincipalCollection = new SimplePrincipalCollection(principal, getName());
    	super.clearCachedAuthenticationInfo(siplePrincipalCollection);
    }
    
    /**
	 * 清空principal用户关联的权限信息，<span style='color:red;'>当修改用户的所属角色时手动调用</span>
	 * @param principal 已认证的用户，可以是Principle中的adminName或adminAccount
	 * @author venson_lew
	 * @date 2016-06-30
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection siplePrincipalCollection = new SimplePrincipalCollection(principal, getName());
		super.clearCachedAuthorizationInfo(siplePrincipalCollection);
	}

	/**
	 * 清空所有权限缓存，<span style="color:red;">当增加用户、增删改角色、菜单（权限）时手动调用</span>
	 * @author venson_lew
	 * @date 2016-06-30
	 */
	public void clearAllCachedAuthorizationInfo() {
		//获取权限缓存
		Cache<Object, AuthorizationInfo> cache = super.getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				cache.remove(key);
			}
		}
	}

}
